/**
* @namespace
* Coordinates the data dependency relationships between bizprocs on a page as well as to optimize the
* overall requests for data from multiple bizprocs and widgets to the same server at the same time.
* This include:
<ul>
 <li>Gathering a list of all the templates referenced by widgets defined at startup as well as initial
	  bizproc data requests from showData() calls and bundle them into one compound service call to help
     reduce the number of requests made to the server at startup, which can slow the overall time it takes
     for an initial page to render.</li>

 <li>When a bizproc action trigger happens, more than one bizproc may want to get data at the same time.
     These will be called together in one compound service call.</li>
</ul>
<p>Note that the ability to use compound bizrules is completely dependent on the server software
implementing the web services.  If the server doesnt support compound bizrules, the web service calls
will happen independently.</p>
* @Depends ksg.base.ui.js
* @meta-type static class
*/
ksg.App.bizProcMgr = {
	/**
	* Helps this class to decide if it should initialize a bizproc when it is registered with it rather than wait until
	* the page is being initialized.
	*/
	pageInitialized: false,
	
	/**
	* Controls if compound rule calls will be used or not.
	*/
	useCompoundCalls: true,
	
	widgets: [],
	bizProcs:[],
	triggerObservers: [],
	rulesetCallObservers: [],
	bizObjSelectedObservers: [],
	
	/**
	* Intended to be called once, at the startup of the page, which will properly coordinate
	* the initialization of the bizprocs and widgets. 
	* Will also coordinate the calls for webservices that can happen at the same time, such as:
	* <ul>
	*	<li>Get all first needed templates from common template server</li>
	*	<li>Inital data requests for data that are independent (no parent id relationship)</li>
	* </ul>
	* @param {Boolean} isPageInit only true once when the page is first being setup.
	*/
	findAndInitDomBizProcs: function(isPageInit) {
		var self = this;
		//Init BizProcs
		$(".ksg-bizproc").each(function() { 
			var $this = $(this);
			var classList =$this.attr('class').split(/\s+/);
			$.each(classList, function(){
				var cName = this.toString().substr(12); //class name we want starts with ksg-bizproc-, but factory doesnt use that part.
				if(ksg.ui.bizprocs[cName]) {
					if($this.data("_bpInit") != true) {
						$this.data("_bpInit", true);
				
						//This block of code is specifically to gather the data- elements and pass them in as plain objects
						var dataParams = {}, attrs = $this.get(0).attributes, attrName, d="data-";
						for (var i = 0; i < attrs.length; i++) {
							var attrib = attrs[i];
							if (attrib.specified == true && (attrName = attrib.name).substr(0,d.length)==d) {
								var partName = attrName.substr(d.length);
								dataParams[partName] = ksg.ui.getElementDataObject($this,partName);
							}
						}
						var dd = $this.data();
						//TODO is dd holds the same things as dataParams, skip the whole for loop above.
					
						ksg.ui.bizprocs[cName]($this.attr('id'), dataParams);
						return false;
					}
				}
			});
		});
		
		if(this.useCompoundCalls) {
			self._startCompoundRuleSet();
			
			if(isPageInit == true) {
				//TODO loop through the widgets and get their template info.  Setup a getTemplate rule call for all those templates
			}
		}
		
		//After looking for all ksg-bizproc elements, they should have self registered and now we can tell them
		//to show their data.
		var bizProcs = this.bizProcs;
		for(var i in bizProcs)
			if(bizProcs[i].isReadyToGetInitialData())
				bizProcs[i].showData();
		//---
		
		if(this.useCompoundCalls)
			ksg.App.ruleMgr.completeCompoundRuleSet(function() {
				self._rulesetCallCompleted();
			});
		
		pageInitialized = true;		
	},//pageInit
	
	
	/**
	* Called when a compound ruleset has completed, which then notifies the ruleset call observers
	* of that fact.
	*/
	_rulesetCallCompleted: function() {
		var observers = this.rulesetCallObservers;
		for(var i in observers)
			if(observers[i].completed)
				observers[i].completed();
	},
	
	/**
	* When the bizProcMgr is setup to use compound rule calls, it will use this to set the
	* ruleMgr to be in a compound rule call and to notify the ruleset call observers that
	* a compound ruleset has started.
	*/
	_startCompoundRuleSet: function() {
		ksg.App.ruleMgr.startCompoundRuleSet();
		var observers = this.rulesetCallObservers;
		for(var i in observers)
			if(observers[i].started)
				observers[i].started();
	},
	
	/**
	* All jQuery widgets that are also used as bizproc views should register themselves with
	* the bizProcMgr using this method.
	* <p>The best time to call this when writing a bizproc view using a jQuery UI Widget is in the _create 
	* method.</p>
	* @param {ksg.ui.IWidget} widget The jQuery widget that implements the IWidget interface.
	*/
	registerWidget: function(widget) {
		var widgets = this.widgets;
		for(var p = 0; p < widgets.length; p++)
			if(widget == widgets[p])
				return;
		widgets.push(widget);
	},
	
	/**
	* When a bizproc view is getting destroyed, it should call this to remove itself from
	* the bizProcMgr.
	* <p>The best time to call this when writing a bizproc view using a jQuery UI Widget is in the destroy
	* method.</p>
	* @param {ksg.ui.IWidget} widget The bizproc view that should be removed.
	*/
	unregisterWidget: function(widget) {
		var newWidgets = [];
		var widgets = this.widgets;
		for(var p = 0; p < widgets.length; p++)
			if(widget != widgets[p])
				newWidgets.push(widgets[p]);
		this.widgets = newWidgets;
	},
	
	/**
	* All bizproc objects on a page should register themselves with the bizproc coordinator.
	* @param {ksg.ui.BizProc} bizProc The bizproc being registered.
	*/
	registerBizProc: function(bizProc) {
		var bizProcs = this.bizProcs;
		for(var p = 0; p < bizProcs.length; p++)
			if(bizProc == bizProcs[p])
				return;
		bizProcs.push(bizProc);
		
		if(this.pageInitialized == true)
			bizProc.showData();
	},
	
	/**
	* Will add the given observer to its internal list, and will be notified
	* when actions are trigger on the page.
	* @param {ksg.ui.ActionTriggerObserver} observer The observer to add.
	*/
	addActionTriggerObserver: function(observer) {
		this.triggerObservers.push(observer);
		return this;
	},
	
	/**
	* Will add the given observer to its internal list, and will be notified
	* when the bizrules are being called by bizprocs from various scenarios occuring.
	* @param {ksg.ui.BizProcRuleCallObserver} observer The observer to add.
	*/
	addRulCallSetObserver: function(observer) {
		this.rulesetCallObservers.push(observer);
		return this;
	},
	
	/**
	* This is a way to register as a global observers of all bizproc having their bizobj being selected.
	* @param {function(bizProc, bizProcId)} observer the handler to get called when a select occurs.
	*/
	addBizObjSelectedObserver: function(observer) {
		this.bizObjSelectedObservers.push(observer);
		return this;
	},
	
	/**
	* It is important for a biproc to call this when it is getting destroyed so that there is not a build
	* up of dead bizprocs still being driven by the bizproc coordinator.
	* This is mostly an issue when a page is using templates which have bizproc elements defined in them
	* and as the user changes something (such as the selected bizobj) and the template changes, the bizprocs can 
	* be cleaned up as they are not getting used any more.
	* <p>This is one of the good reasons why a bizproc is defined like a widget on a dom element.  jQuery ui
	* already takes cares of calling its widget _create and destroy methods when their dom element is going away.
	* The bizproc should be using a mechanism like that to control life time.</p>
	* @param {ksg.ui.BizProc} bizProc the bizproc being unregistered.
	*/
	unregisterBizProc: function(bizProc) {
		var newBizProcs = [];
		var bizProcs = this.bizProcs;
		for(var p = 0; p < bizProcs.length; p++)
			if(bizProc != bizProcs[p])
				newBizProcs.push(bizProcs[p]);
		this.bizProcs = newBizProcs;
	},
	
	/**
	* Called by a bizproc when one of its bizobj was selected from one of its bizobj lists.
	* <p>This method helps to coordinate observers of this process being called as well 
	* as to allow compound bizrules to be used when this important type of event happens.</p>
	* @param {ksg.ui.BizProc} bizProc The bizproc calling this method.
	* @param {Object} bizObjId The id of the bizobj that was selected.
	* @param {function} handler The handler from the bizproc to actually do the work of selecting
	* the bizobj and showing it.
	*/
	bizObjSelected: function(bizProc, bizObjId, handler) {
		var self = this;
		for(var i in self.bizObjSelectedObservers)
			self.bizObjSelectedObservers[i](bizProc, bizObjId);
			
		if(self.useCompoundCalls)
			self._startCompoundRuleSet();
		
		handler();
	
		if(self.useCompoundCalls)
			ksg.App.ruleMgr.completeCompoundRuleSet(function() { 
				self._rulesetCallCompleted();
			});	
	},
	
	/**
	* When an "action trigger" ui widget is triggered (such as clicking a button or choosing an option in a select drop down).
	* and wants to have the bizproc actions get triggered, it should cal this method, which will help to coordinate
	* observers of an action event like this with the action itself.
	* <p>This method really just deals with managing the observers of an action trigger event, not the actual
	* calling of the bizproc action methods.  This is because the type of action widget may do that processing differently
	* so it is no something that can be done in here.</p>
	* @param {String} actionStr The comma delimited list of bizproc id.actionName to be trigger.
	* @param {function} handler The function that will do the work of actually triggering the bizproc actions.
	*/
	actionTriggered: function(actionStr, handler) {
		var self = this;
		for(var i in self.triggerObservers)
			if(self.triggerObservers[i].started)
				self.triggerObservers[i].started(actionStr);

		if(this.useCompoundCalls)
			self._startCompoundRuleSet();
		
		handler();
		
		if(this.useCompoundCalls)
			ksg.App.ruleMgr.completeCompoundRuleSet(function() { 
				self._rulesetCallCompleted();
				for(var i in self.triggerObservers)
					if(self.triggerObservers[i].completed)
						self.triggerObservers[i].completed(actionStr);
			});	
	},

	/**
	* Will build an array of bizproc widgets that match a given type.
	* @param {ksg.ui.BizProcWidgetType} widgetType the type of bizproc to filter with.
	* @param {String} bizProcId the id of the bizproc to get the widgets for.
	*/
	_getWidgets: function(widgetType, bizProcId) {
		var widgets = [];
		
		for(var i in this.widgets) {
			var widget = this.widgets[i];
			if(widget.getWidgetType() == widgetType && (!(bizProcId) || widget.getParentBizProcId() == bizProcId) )
				widgets.push(widget);
		}

		return widgets;
	},
	
	/**
	* A bizproc will use this method to get the list of bizproc widgets of a given type.
	* <p>The bizproc should never cache the list of widgets in a long term variable, but instead 
	* should use this method to get the active list at the moment.</p>
	* @param {String} bizProc Id The id of the bizProc to get the widgets for.
	* @param {ksg.ui.BizProcWidgetType} widgetType The type of widget that should be returned in the list.
	* @returns {array of ksg.ui.IWidget} An array of widgets that match, or an emptry array if none match.
	*/
	getBizProcWidgets: function(bizProcId, widgetType) {
		return this._getWidgets(widgetType,bizProcId);
	},
	
	/**
	* Will return the ksg.ui.BizProc object that has an id that matches
	* the provided id.
	* <p>No code should cache a bizproc object in a long term variable.  Rather,
	* they should cache the id, and use this method to find it.</p>
	* @param {String} bizProcId The id of the bizproc to find.
	* @returns {ksg.ui.BizProc} The bizproc with matching id, or null no bizobj has the given id.
	*/
	getBizProc: function(bizProcId) {
		var bizProcs = this.bizProcs;
		for(var i in bizProcs)
			if(bizProcs[i].id == bizProcId)
				return bizProcs[i];
		return null;
	},
	
	/**
	* When a bizproc's data changes in some way, it should call this method in order to allow
	* child bizprocs to be refresh as a result.  This is one of the core responsibilities of
	* the bizProcMgr.
	* @param {ksg.ui.BizProc} bizProc The bizproc that is calling this method.
	*/
	bizProcDataAvailable: function(bizProc) {
		var bizProcs = this.bizProcs;
		var isParentOf = 
			function(sourceBP) {
				var parents = sourceBP.parentBizProcs;
				if(parents)
					for(var i in parents)
						if(parents[i].bizProcId == bizProc.id)
							return true;
				return false;
			};
		
		for(var i in bizProcs) {
			if(isParentOf(bizProcs[i]) && (bizProcs[i].initState == 2 || bizProcs[i].isReadyToGetInitialData())) {
				bizProcs[i].clearData(true,true);
				bizProcs[i].showData();
			}
		}
	}
	
};

